// Description: 业务缓存插件(采用redis进行的分布式缓存实现)
package xcache

import (
	"encoding/json"
	"reflect"
	"time"

	"github.com/zeromicro/go-zero/core/stores/cache"
)

type (
	SpanCache interface {
		// 针对列表数据的缓存（针对app端列表数据的缓存）（内部默认缓存五分钟）
		List(key string, v interface{}, fn func() (v interface{}, hasMore bool, err error)) (hasMore bool, err error)
		// 针对详情的缓存(复杂组合数据对象的详情)
		Detail(key string, data interface{}, fn func() (v interface{}, err error)) (err error)
		// 删除key
		Del(key string)
	}

	defaultSpanCache struct {
		cache          cache.Cache
		expiry         int64 // 过期时间(秒)
		notFoundExpiry int64 // 无数据的过期时间(秒)
	}
)

func NewSpanCache(c cache.CacheConf) SpanCache {
	return &defaultSpanCache{
		expiry:         int64(defaultExpiry.Seconds()),
		notFoundExpiry: int64(defaultNotFoundExpiry.Seconds()),
		cache:          cache.NewNode(c[0].NewRedis(), sharedCalls, stats, ErrNotFound, cache.WithExpiry(defaultExpiry), cache.WithNotFoundExpiry(defaultNotFoundExpiry)),
	}
}

func NewSpanCacheWithExpiry(c cache.CacheConf, expiry time.Duration) SpanCache {
	return &defaultSpanCache{
		expiry:         int64(expiry.Seconds()),
		notFoundExpiry: int64(defaultNotFoundExpiry.Seconds()),
		cache:          cache.NewNode(c[0].NewRedis(), sharedCalls, stats, ErrNotFound, cache.WithExpiry(expiry), cache.WithNotFoundExpiry(defaultNotFoundExpiry)),
	}
}

// 针对列表数据的缓存（针对app端列表数据的缓存）（内部默认缓存五分钟）
// 内存缓存->数据查询->内存缓存设置
func (bc *defaultSpanCache) List(key string, data interface{}, fn func() (interface{}, bool, error)) (bool, error) {
	value := spanCacheRet{}

	err := bc.cache.TakeWithExpire(&value, key, func(v interface{}, expire time.Duration) error {
		list, hasMore, e := fn()
		if e != nil {
			return e
		}

		if val := reflect.ValueOf(list); !val.IsValid() || val.IsNil() {
			v.(*spanCacheRet).Value = "[]"
			v.(*spanCacheRet).HasMore = false
		} else {
			body, _ := json.Marshal(list)
			v.(*spanCacheRet).Value = string(body)
			v.(*spanCacheRet).HasMore = hasMore
		}

		return nil
	})
	if err != nil {
		return false, err
	}

	_ = json.Unmarshal([]byte(value.Value), data)

	return value.HasMore, nil
}

// 针对详情的缓存(复杂组合数据对象的详情)
func (bc *defaultSpanCache) Detail(key string, data interface{}, fn func() (v interface{}, err error)) error {
	value := spanCacheRet{}
	err := bc.cache.TakeWithExpire(&value, key, func(v interface{}, expire time.Duration) error {
		object, e := fn()
		if e != nil {
			return e
		}

		if val := reflect.ValueOf(object); !val.IsValid() || val.IsNil() {
			v.(*spanCacheRet).Value = "{}"
		} else {
			body, _ := json.Marshal(object)
			v.(*spanCacheRet).Value = string(body)
		}

		return nil
	})
	if err != nil {
		return err
	}

	if value.Value == "{}" {
		return ErrNotFound
	}

	_ = json.Unmarshal([]byte(value.Value), data)

	return nil
}

// 删除
func (bc *defaultSpanCache) Del(key string) {
	_ = bc.cache.Del(key)
}
